#include <stdio.h>
#include <GLUnity.h>
const   int windowWidth     = 256;
const   int windowHeight    = 256;

static int g_renderMode = 1;

GLenum  checkForError   (   const char* log)
{
    GLenum err = ::glGetError();
    if(GL_NO_ERROR!=err){
        const char* szErrorInfo = (const char*)::gluErrorString(err);
        printf("Opengl Error:%s", szErrorInfo);
        if(NULL != log){
            printf("\t%s", log);
        }
        printf("\r\n");
    }
    return err;
}

//
void    opacitySourceRender (   int w, 
                                int h   )
{
    float tileVertices0[]={0, h/2, w, h/2, w, h*3/4, 0, h*3/4};
    ::glColor4f(1, 0.0f, 0.0f, 0.5f);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices0);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    //
    float tileVertices1[]={w/4.0f,0,w/2.0f,0,w/2.0f,h,w/4.0f,h};
    ::glColor4f(1, 0, 0, 0.5f);
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}

//
void    opacityRectUnionRender  (   int w, 
                                    int h   )
{
    ::glEnable(GL_STENCIL_TEST);
    ::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    ::glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
    ::glStencilFunc(GL_EQUAL, 0x0, 0x01);
    ::glEnable(GL_BLEND);
    ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    float tileVertices0[]={0.0f, h/2.0f, w, h/2.0f, w, h*3.0f/4.0f, 0.0f, h*3.0f/4.0f};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices0);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    //
    float tileVertices1[]={w/4.0f, 0.0f, w/2.0f, 0.0f, w/2.0f, h, w/4.0f, h};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    ::glDisable(GL_STENCIL_TEST);
}

//
void    opacityRectDifferenceRender (   int w, 
                                        int h   )
{
    ::glEnable(GL_STENCIL_TEST);

    ::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    ::glStencilFunc(GL_ALWAYS, 0, 0xff);
    ::glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);

    float tileVertices0[]={0,h/2,w,h/2,w,h*3/4,0,h*3/4};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices0);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.1f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    ::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    ::glStencilFunc(GL_EQUAL, 0x00, 0x01);
    ::glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);

    float tileVertices1[]={w/4, 0, w/2, 0, w/2, h, w/4, h};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    ::glDisable(GL_STENCIL_TEST);
}

//
void    opacityRectXorRender    (   int w, 
                                    int h   )
{
    ::glEnable(GL_STENCIL_TEST);
    ::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    ::glStencilOp(GL_INCR, GL_INCR, GL_INCR);
    ::glStencilFunc(GL_ALWAYS, 0x00, 0xff);

    float tileVertices0[]={0,h/2,w,h/2,w,h*3/4,0,h*3/4};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices0);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    float tileVertices1[]={w/4,0,w/2,0,w/2,h,w/4,h};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(4.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    ::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    ::glStencilFunc(GL_EQUAL, 0x01, 0x01);
    ::glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);

    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices0);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    ::glDisable(GL_STENCIL_TEST);
}

void    opacityRectIntersetRender   (   int w, int h)
{
    ::glEnable(GL_STENCIL_TEST);
    ::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

    ::glStencilOp(GL_INCR, GL_INCR, GL_INCR);
    ::glStencilFunc(GL_ALWAYS, 0x00, 0xff);

    float tileVertices0[]={0,h/2,w,h/2,w,h*3/4,0,h*3/4};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices0);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    float tileVertices1[]={w/4,0,w/2,0,w/2,h,w/4,h};
    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    ::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    ::glStencilFunc(GL_EQUAL, 0x02, 0x03);
    ::glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
    

    ::glVertexPointer(2, GL_FLOAT, 0, tileVertices1);
    ::glEnableClientState(GL_VERTEX_ARRAY);
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glDrawArrays(GL_TRIANGLE_FAN, 0, 4);


    ::glDisable(GL_STENCIL_TEST);
}

void    displayFunc (   void    )
{
    ::glCullFace(GL_BACK);

    float destAlpha = 0.5f;
    ::glClearColor(0, 0, 0, 0.5f);
    ::glClearStencil(0);
    ::glClearDepth(0.0f);
    ::glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);

    ::glEnable(GL_BLEND);
    ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

#if 0
    ::glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    ::glBegin(GL_QUADS);
    ::glVertex2f(10, 10);
    ::glVertex2f(100, 10);
    ::glVertex2f(100, 100);
    ::glVertex2f(10, 100);
    ::glEnd();
#endif

    switch (g_renderMode)
    {
    case 1:
        opacitySourceRender(windowWidth, windowHeight);
        break;
    case 2:
        opacityRectUnionRender(windowWidth, windowHeight);
        break;
    case 3:
        opacityRectDifferenceRender(windowWidth, windowHeight);
        break;
    case 4:
        opacityRectXorRender(windowWidth, windowHeight);
        break;
    case 5:
        opacityRectIntersetRender(windowWidth, windowHeight);
        break;
    default:
        break;
    }
    ::glutSwapBuffers();
    checkForError("swap");
}

void    reshapeFunc (   int w, 
                        int h   )
{
    ::glMatrixMode(GL_PROJECTION);
    ::glLoadIdentity();
    ::glOrtho(0.0f, (GLfloat)windowWidth, 0.0f, (GLfloat)windowWidth, -1.0f, 1.0f);
    ::glMatrixMode(GL_MODELVIEW);
    ::glLoadIdentity();
    ::glViewport(0, 0, windowWidth, windowHeight);
}

void    mouseFunc   (   int button, 
                        int state, 
                        int x, 
                        int y   )
{
    //
}

void    keyboardFunc    (   unsigned char   key, 
                            int             x, 
                            int             y   )
{
    ::glutPostRedisplay();
}

void    init    (   void    )
{
    ::glMatrixMode(GL_PROJECTION);
    ::glLoadIdentity();
    ::glOrtho(0.0f, (GLfloat)windowWidth, 0.0f, (GLfloat)windowHeight, -1.0f, 1.0f);
    ::glMatrixMode(GL_MODELVIEW);
    ::glLoadIdentity();
    ::glViewport(0, 0, windowWidth, windowHeight);
    ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    ::glEnable(GL_CULL_FACE);
}

void menuProc(int menuItem)
{
    g_renderMode = menuItem;
    ::glutPostRedisplay();
}

int main(int argc, char** argv)
{
    ::glutInit(&argc, argv);
    ::glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH|GLUT_STENCIL);

    ::glutInitWindowSize(windowWidth, windowHeight);
    ::glutCreateWindow("Region");
    if(GLEW_OK==::glewInit()){
        ::glutDisplayFunc(displayFunc);
        ::glutReshapeFunc(reshapeFunc);
        ::glutMouseFunc(mouseFunc);
        ::glutKeyboardFunc(keyboardFunc);
        ::glutCreateMenu(menuProc);
        ::glutAddMenuEntry("Source", 1);
        ::glutAddMenuEntry("Union", 2);
        ::glutAddMenuEntry("Difference", 3);
        ::glutAddMenuEntry("Xor", 4);
        ::glutAddMenuEntry("Interset", 5);
        ::glutAttachMenu(GLUT_RIGHT_BUTTON);
        init();
        ::glutMainLoop();
        return 0;
    }
    else{
        return -1;
    }
}